package com.gitee.l0km.javadocreader;

import java.io.ByteArrayOutputStream;
import java.io.PrintStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.Executable;
import java.lang.reflect.Field;
import java.lang.reflect.Member;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.regex.Pattern;

import com.google.common.collect.Lists;
import com.gitee.l0km.aocache.annotations.AoWeakCacheable;
import com.gitee.l0km.javadocreader.internal.TypeNames;
import com.google.common.base.Function;
import com.google.common.base.Predicate;
import com.google.common.base.Strings;
import com.google.common.collect.Collections2;
import com.google.common.collect.HashBasedTable;
import com.google.common.collect.Maps;

import javax.lang.model.element.TypeElement;
import javax.lang.model.element.Element;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.VariableElement;

import com.sun.source.util.DocTrees;
import com.sun.source.doctree.*;

import jdk.javadoc.doclet.DocletEnvironment;

import javax.lang.model.type.TypeMirror;
import javax.lang.model.util.ElementFilter;
import javax.lang.model.util.Elements;
import javax.lang.model.util.Types;

import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkArgument;;

public class ExtClassDoc {
	public static final String NEW_LINE = System.getProperty("line.separator");
	public static enum Type{
		CLASS,METHOD,FIELD,NONE(0),ALL((~0));
		static {
			// 修改 ALL 的mask 值
			int mask = 0;
			for(Type v :Type.values()){
				if(1 == Integer.bitCount(v.mask))mask |=v.mask;
			}
			try {
				Field field = Type.class.getDeclaredField("mask");
				field.setAccessible(true);
				field.set(ALL, mask);
			} catch (Exception e) {
				throw new ExceptionInInitializerError(e);
			}
			// 检查所有组合mask值的合法性,超出范围则抛出异常
			for(Type v :Type.values()){
				if( Integer.bitCount(v.mask) > 1 && v != ALL){
					if(((~ALL.mask)&v.mask) != 0)
						throw new ExceptionInInitializerError(String.format("%s: %s,invalid mask, out of ALL scope",v.name(), 
								Integer.toBinaryString(v.mask)));
				}
			}
		}
		private final int mask;
		Type(){
			this.mask=(1<<this.ordinal());
		}

		Type(int mask){
			checkArgument(1 != Integer.bitCount(mask), "bit count of mask must be not be 1");
			this.mask=mask;
		}
		public boolean check(Integer v){
			return null == v? false :mask == (mask & v);
		}
		public boolean check(Type v){
			return null == v ? false: check(v.mask);
		}
		public int or(int v){
			return mask | v;
		}
		public int reset(int v){
			return (~mask) & v;
		}
		public static int sum(Type ...values){
			if(null==values)return 0;
			int m=0;
			for(Type v:values) if(null != v)m|=v.mask;
			return m;
		}
		public static int sum(Collection<Type>values){
			// 过滤掉null元素
			return null == values ? 0 :sum(Collections2.filter(values, new Predicate<Type> (){
				@Override
				public boolean apply(Type input) {
					return null !=input;
				}}).toArray(new Type[0]));
		}
		public static int sumOfString(Collection<String>values){
			return null == values ? 0 :sum(Collections2.transform(values, new Function<String,Type>(){
				@Override
				public Type apply(String input) {
					try{
						return Type.valueOf(input);
					}catch(Exception e){
						return null;
					}
				}}));
		}
		public static List<Type>checkAll(int value){
			ArrayList<Type> list = new ArrayList<Type>();
			for(Type v:Type.values()){
				if(v.check(value) && Integer.bitCount(v.mask) == 1)
					list.add(v);
			}	
			return list;
		}
	}
	public static enum Action{
		ADD,OVERWRITE,APPEND
	}
	public static enum AddColumn{
		ACTION,SCOPE
	}
	final DocletEnvironment docEnv;
	final TypeElement classDoc;
	final Elements elementUtils;
	final Types typeUtils;
	final DocTrees docTrees;
	/** 缩进字符串 */
	private static String indent = "    ";
	/** 输出comment时要排除的{@link DocTree}名字,比如{@code '@throws'} */
	private final Map<String,Integer> excludeTags=Collections.synchronizedMap(new HashMap<String,Integer>());
	/** 输出comment时要添加在commentText中的内容 */
	private final HashBasedTable<String,AddColumn,Object> additionalTextTable = HashBasedTable.create();
	private final CommentTextRender render;
	public ExtClassDoc(DocletEnvironment docEnv,TypeElement classDoc) {
		super();
		this.classDoc = checkNotNull(classDoc, "classDoc is null");
		this.docEnv = checkNotNull(docEnv, "docEnv is null");
		this.elementUtils=docEnv.getElementUtils();
		this.typeUtils=docEnv.getTypeUtils();
		this.docTrees = docEnv.getDocTrees();
    	this.render = new CommentTextRender();
	}

	/**
	 * 如果两个类型字符串匹配，返回{@code true}，否则返回{@code false}
	 * @param docType
	 * @param type
	 */
	@AoWeakCacheable
	private boolean equalType(TypeMirror docType,java.lang.reflect.Type type) {
		String typeName = TypeNames.getTypeName(type).replaceAll("\\s+", "");
		return typeName.equals(docType.toString().replaceAll("\\s+", ""));
	}
	/**
	 * 如果两个类型字符串匹配，返回{@code true}，否则返回{@code false}
	 * @param typeElement
	 * @param clazz
	 */
	@AoWeakCacheable
	private boolean equalType(TypeElement typeElement,Class<?> clazz) {
		String typeName = TypeNames.getTypeName(clazz);
		return typeName.equals(typeElement.getQualifiedName().toString());
	}
	/**
	 * 检查两个方法对象的签名是否匹配<br>
	 * @param member
	 * @param doc
	 * @return 不匹配返回 {@code false} ,匹配返回 {@code true}
	 */
	private boolean match(Member member, Element doc) {
		if (!member.getName().equals(doc.getSimpleName().toString())){
			return false;
		}
		if(member instanceof Field) {
			return true;
		}
		checkArgument(member instanceof Executable,"INVALID member type %s,Method or Constructor required",member.getClass().getSimpleName());
		java.lang.reflect.Type[] paramTypes = ((Executable)member).getGenericParameterTypes();
		checkArgument(doc instanceof ExecutableElement,"INVALID doc type %s,ExecutableElement required",doc.getClass().getSimpleName());
		List<? extends VariableElement> parameters = ((ExecutableElement)doc).getParameters();
		if (paramTypes.length != parameters.size()) {
			return false;
		}
		for (int i = 0; i < paramTypes.length; ++i) {
			if(!equalType(parameters.get(i).asType(),paramTypes[i])) {
				return false;
			}
		}
		return true;
	}
	/**
	 * 在{@link TypeElement}中查找与 {@link Method} 匹配的{@link ExecutableElement}<br>
	 * 如果没有在当前方法上找到注释且是重写方法，则尝试向上父类查找父类方法
	 * @param method  
	 * @return 没有找则返回{@code null}
	 * @see #getMemberDoc(TypeElement, Member)
	 */	
	public ExecutableElement getMethodDoc(Method method) {
		ExecutableElement doc = (ExecutableElement) getMemberDoc(classDoc,method);
		while(null != doc && Strings.isNullOrEmpty(elementUtils.getDocComment(doc))){
			// 如果没有注释,向上父类查找被重写的方法
			doc = findMethodsInSuperClass(doc);
		}
		return doc;
	}
	private ExecutableElement findMethodsInSuperClass(ExecutableElement methodDoc) {
		TypeMirror typeMirror = ((TypeElement) methodDoc.getEnclosingElement()).getSuperclass();
		return findMethodsInSuperClass(typeMirror,methodDoc);
	}
    private ExecutableElement findMethodsInSuperClass(TypeMirror superclass, ExecutableElement method) {
        if (null == superclass ||  superclass.getKind().isPrimitive() || superclass.toString().equals("java.lang.Object")) {
            return null; // 到达根类
        }

        TypeElement superClassElement = (TypeElement) typeUtils.asElement(superclass);
        List<? extends Element> enclosedElements = superClassElement.getEnclosedElements();

        for (Element element : enclosedElements) {
            if (element instanceof ExecutableElement) {
                ExecutableElement superMethod = (ExecutableElement) element;
                // 检查该方法是否重写
                if (elementUtils.overrides(method,superMethod, (TypeElement) method.getEnclosingElement())) {
                	return superMethod;
                }
            }
        }

        // 递归查找超类
        return  findMethodsInSuperClass(superClassElement.getSuperclass(), method);
    }
	/**
	 * 
	 * [递归]在{@link TypeElement}中递归查找与method匹配的{@link Element}对象<br>
	 * @see #findMember(TypeElement, Member)
	 */
	private Element getMemberDoc(TypeElement classDoc,Member member) {
		if (null == classDoc || null == member){
			return null;
		}
		Element matched = findMember(classDoc, member);
		if(matched == null){
			return getMemberDoc((TypeElement) typeUtils.asElement(classDoc.getSuperclass()), member);
		}
		return matched;
	}
	/**
	 * 在{@link TypeElement}中查找与 {@link Executable} 匹配的{@link ExecutableElement}<br>
	 * @param executable  executable
	 * @return 没有找则返回{@code null}
	 * @see #getMemberDoc(TypeElement, Member)
	 */	
	public ExecutableElement getExecutableMemberDoc(Executable executable) {
		return (ExecutableElement) getMemberDoc(classDoc,executable);
	}
	/**
	 * 在{@link TypeElement}中查找与 {@link Member} 匹配的{@link Element}<br>
	 * @param member  member
	 * @return 没有找则返回{@code null}
	 * @see #getMemberDoc(TypeElement, Member)
	 */	
	public Element getMemberDoc(Member member) {
		return getMemberDoc(classDoc,member);
	}
	/**
	 * 在{@link TypeElement}中查找指定方法或构造方法的参数名
	 * @param member Method or Constructor
	 * @return 参数名列表,找不到返回null
	 */
	public String[] getParamerNames(Member member) {
		ExecutableElement memberDoc = getExecutableMemberDoc((Executable)member);
		if(memberDoc == null){
			return null;
		}
		List<? extends VariableElement> parameters = memberDoc.getParameters();
		String[] names = new String[parameters.size()];
		for(int i = 0; i < names.length; ++i){
			names[i] = parameters.get(i).getSimpleName().toString();
		}
		return names;
	}
	/**
	 * 在{@link TypeElement}中递归查找与name匹配的{@link VariableElement}<br>
	 * @param classDoc 
	 * @param name field name
	 * @return 没有找则返回{@code null}
	 */	
	private VariableElement getFieldDoc(TypeElement classDoc, String name) {
		if (null == classDoc || null == name) {
			return null;
		}
		Optional<VariableElement> opt = ElementFilter.fieldsIn(classDoc.getEnclosedElements()).stream()
				.filter(e -> e.getSimpleName().contentEquals(name)).findFirst();
		if (opt.isPresent()) {
			return opt.get();
		}
		TypeMirror superClass = classDoc.getSuperclass();
		return getFieldDoc(null == superClass ? null : (TypeElement) typeUtils.asElement(superClass), name);
	}
	/**
	 * 在{@link TypeElement}中查找与name匹配的{@link VariableElement}<br>
	 * @param name field name
	 * @return 没有找则返回{@code null}
	 */	
	public VariableElement getFieldDoc(String name) {
		return getFieldDoc(classDoc,name);
	}
	ParamTree paramTagOf(ExecutableElement methodDoc,String name){
		if(null == methodDoc) {
			return null;
		}
			if(!Strings.isNullOrEmpty(name)){
				DocCommentTree docCommentTree = docTrees.getDocCommentTree(methodDoc);
				return new BlockTagExtracter().extract(docCommentTree).paramTag(name);
			}
			return null;

	}
	/**
	 * 类注释如果有 '@deprecated' 注解则返回{@code true},否则返回{@code false}
	 */
	public boolean isDeprecated() {
		return isDeprecated(this.classDoc) ;
	}
	/**
	 * 注释对象{@code doc} 中如果有 '@deprecated' 注解则返回{@code true},否则返回{@code false}
	 * @param doc
	 */
	public boolean isDeprecated(Element doc) {
		if(null == doc) {
			return false;
		}
		return elementUtils.isDeprecated(doc);
	}

	/**
	 * 如果{@link Member}的注释中如果有 '@deprecated' 注解则返回{@code true},否则返回{@code false}
	 * 
	 * @param member
	 * @since 1.3.0
	 */
	public boolean isDeprecated(Member member) {
		return isDeprecated(getMemberDoc(member));
	}

	/**
	 * 在{@link TypeElement}中查找与method匹配的{@link ExecutableElement}对象<br>
	 * 没找到匹配的对象则返回{@code null}
	 * @param classDoc
	 * @param member 
	 */
	private Element findMember(TypeElement classDoc,Member member) {
		if (null == classDoc || null == member){
			return null;
		}
		if(!equalType(classDoc,member.getDeclaringClass())) {
			return null;
		}
		if (member instanceof Field) {
			return ElementFilter.fieldsIn(classDoc.getEnclosedElements()).stream().filter(e -> match(member, e))
					.findFirst().orElse(null);
		} else if (member instanceof Method) {
			return ElementFilter.methodsIn(classDoc.getEnclosedElements()).stream().filter(e -> match(member, e))
					.findFirst().orElse(null);
		} else if (member instanceof Constructor<?>) {
			return ElementFilter.constructorsIn(classDoc.getEnclosedElements()).stream().filter(e -> match(member, e))
					.findFirst().orElse(null);
		} else {
			throw new IllegalArgumentException(String.format(
					"INVALID member type %s,Field,Method or Constructor required", member.getClass().getSimpleName()));
		}
	}

	/**
	 * 输出当前类的类及方法注释信息
	 * @param out 输出对象
	 */
	public void output(PrintStream out) {
		out.println(formatComment(classDoc, false));
		out.println(classDoc);
		for (Element e : ElementFilter.methodsIn(classDoc.getEnclosedElements()) ) {
			ExecutableElement method = (ExecutableElement)e;
			out.println(formatComment(method, true));
			out.printf("%s%s\n", indent, method.toString());
		}
	}

	public String output() {
		ByteArrayOutputStream out = new ByteArrayOutputStream();
		output(new PrintStream(out));
		return out.toString();
	}

	private static final String commentBody = "/**" + NEW_LINE + "cmt */" + NEW_LINE;
	private  static final Type typeOfDoc(Object doc){
		if(doc instanceof TypeElement){
			return Type.CLASS;
		}
		if(doc instanceof ExecutableElement){
			return Type.METHOD;
		}
		if(doc instanceof VariableElement){
			return Type.FIELD;
		}
		throw new UnsupportedOperationException();
	}

	private Set<String> select(final Type type,final Action action){
		return Maps.filterValues(additionalTextTable.rowMap(), new Predicate<Map<AddColumn, Object>>(){
			@Override
			public boolean apply(Map<AddColumn, Object> input) {
				return (input.get(AddColumn.ACTION)==action)&& type.check((Integer)input.get(AddColumn.SCOPE));
			}}).keySet();
	}
	private final String commentText(StringBuffer buffer,String originText,Type type){
		Set<String> addText = select(type,Action.ADD);
		Set<String> overwriteText = select(type,Action.OVERWRITE);
		Set<String> appendText = select(type,Action.APPEND);
		for(String text:addText)buffer.append(text).append(NEW_LINE);
		for(String text:overwriteText)buffer.append(text).append(NEW_LINE);
		if(overwriteText.isEmpty() && !originText.isEmpty())
			buffer.append(originText).append(NEW_LINE);
		for(String text:appendText)buffer.append(text).append(NEW_LINE);
		return buffer.toString();
	}
	/**
	 * 输出格式化的注释信息
	 * 
	 * @param doc
	 *            {@link com.sun.tools.javadoc.ClassDocImpl} 或 {@link com.sun.tools.javadoc.MethodDocImpl}实例
	 * @param needIndent 是否缩进
	 * @param withBody 是否添加注释体
	 * @param withLinePrefix 是否添行注释前缀
	 * @param withTags 是否添加标签
	 * @return
	 */
	private final String formatComment0(Element doc, boolean needIndent, boolean withBody, boolean withLinePrefix, boolean withTags) {
		checkNotNull(doc, "doc is null");
		DocCommentTree docCommentTree = docTrees.getDocCommentTree(doc);
		StringBuffer buffer = new StringBuffer();
		if(null != docCommentTree) {
			Type type = typeOfDoc(doc);
			commentText(buffer,render.render(docCommentTree),type);
			if(withTags) {
				for (DocTree tag : docCommentTree.getBlockTags()) {
					BlockTagTree blockTag=(BlockTagTree)tag;
					if( ! type.check(excludeTags.get("@"+blockTag.getTagName())))
						buffer.append(render.withFullTag().render(blockTag)).append(NEW_LINE);
				}
			}
		}
		String cmt = buffer.toString();
		if (!cmt.isEmpty()) {
			cmt = Pattern.compile("(\r\n|\n|\r)\\s*", Pattern.MULTILINE).matcher(cmt).replaceAll(NEW_LINE);
			if(withBody || withLinePrefix){
				cmt = Pattern.compile("^", Pattern.MULTILINE).matcher(cmt).replaceAll(" * ");
			}
			if(withBody){
				cmt = commentBody.replace("cmt", cmt);
			}
			if (needIndent)
				cmt = Pattern.compile("^", Pattern.MULTILINE).matcher(cmt).replaceAll(indent);
		}
		return cmt;
	}
	/**
	 * 输出格式化的注释信息
	 * 
	 * @param doc
	 *            {@link Element} 实例
	 * @param needIndent
	 *            是否缩进
	 * @return 格式的注释信息字符串
	 */
	public final String formatComment(Element doc, boolean needIndent) {
		return formatComment0(doc, needIndent, true, true, true);
	}
	/**
	 * 输出格式化的注释信息,返回以行为单位的字符串表
	 * 
	 * @param doc
	 *            {@link Element} 实例
	 * @param needIndent
	 *            是否缩进
	 * @param withLinePrefix 是否添行注释前缀
	 * @param withTags 是否添加标签
	 * @return 以行为单位的格式的注释信息字符串
	 */
	public final List<String> formatCommentAsList(Element doc, boolean needIndent, boolean withLinePrefix, boolean withTags) {
		String cmtstr = formatComment0(doc, needIndent, false, withLinePrefix, withTags);
		if(cmtstr.isEmpty()){
			return Collections.emptyList();
		}
		return Lists.newArrayList(cmtstr.split(NEW_LINE));
	}
	public String getClassComment(boolean withBody, boolean withLinePrefix, boolean withTags) {
		return formatComment0(classDoc, false,withBody,withLinePrefix, withTags);
	}

	public String getMethodComment(Method method,boolean withBody, boolean withLinePrefix, boolean withTags) {
		ExecutableElement doc = getMethodDoc(method);
		return null == doc? null : formatComment0(doc, true,withBody,withLinePrefix, withTags);
	}
	public String getFieldComment(String name,boolean withBody, boolean withLinePrefix, boolean withTags) {
		VariableElement doc = getFieldDoc(name);
		return null == doc? null : formatComment0(doc, true,withBody,withLinePrefix, withTags);
	}
	
	public String getClassComment() {
		return getClassComment(true, true, true);
	}

	public String getMethodComment(Method method) {
		return getMethodComment(method, true, true, true);
	}
	public String getFieldComment(String name) {
		return getFieldComment(name, true, true, true);
	}
	
	public List<String> getClassCommentAsList(boolean needIndent, boolean withLinePrefix, boolean withTags) {
		return formatCommentAsList(classDoc, needIndent, withLinePrefix, withTags);
	}

	public List<String> getMethodCommentAsList(Method method,boolean needIndent, boolean withLinePrefix, boolean withTags) {
		ExecutableElement doc = getMethodDoc(method);
		return null == doc? null :formatCommentAsList(doc, needIndent, withLinePrefix, withTags);
	}
	public List<String> getFieldCommentAsList(String name,boolean needIndent, boolean withLinePrefix, boolean withTags) {
		VariableElement doc = getFieldDoc(name);
		return null == doc? null :formatCommentAsList(doc, needIndent, withLinePrefix, withTags);
	}
	
	public List<String> getClassCommentAsList() {
		return getClassCommentAsList(false, true, true);
	}

	public List<String> getMethodCommentAsList(Method method) {
		return getMethodCommentAsList(method,true, true, true);
	}
	public List<String> getFieldCommentAsList(String name) {
		return getFieldCommentAsList(name, true, true, true);
	}
	
	/**
	 * 在{@link TypeElement}中查找与method匹配的{@link ExecutableElement},返回注释信息字符串<br>
	 * @param method  method
	 * @return 没有方法找则返回空字符串
	 * @see #getMethodDoc(Method)
	 */
	@AoWeakCacheable
	public String commentTextOf(Method method) {
		ExecutableElement methodDoc = getMethodDoc(method);
		if(methodDoc == null) {
			return "";
		}
		DocCommentTree docCommentTree = docTrees.getDocCommentTree(methodDoc);
		return render.render(docCommentTree);
	}
	/**
	 * 返回当前类的注释信息字符串
	 */
	@AoWeakCacheable
	public String commentText() {
		DocCommentTree docCommentTree = docTrees.getDocCommentTree(classDoc);
		return render.render(docCommentTree);
	}
	/**
	 * 在{@link TypeElement}中查找方法的指定参数名称的注释信息字符串<br>
	 * @param method  method
	 * @param name 参数名称
	 * @return 输入参数为{@code null}或没有方法找或没有找到参数名则返回空字符串
	 * @see #getMethodDoc(Method)
	 * @see #paramTagOf(ExecutableElement, String)
	 */
	@AoWeakCacheable
	public String parameterCommentOf(Method method,String name) {
		ParamTree paramTag = paramTagOf(getMethodDoc(method), name);
		return paramTag == null ? "" : render.render(paramTag);
	}
	/**
	 * @return indent
	 */
	public static String getIndent() {
		return indent;
	}

	/**
	 * @param indent
	 *            要设置的 indent
	 */
	public static void setIndent(String indent) {
		if (null != indent)
			ExtClassDoc.indent = indent;
	}

	/**
	 * @param excludeTags 要设置的 excludeTags
	 */
	public synchronized void setExcludeTags(Map<String,Integer> excludeTags) {
		if(null != excludeTags){
			excludeTags.clear();
			excludeTags .putAll(excludeTags);
		}
	}
	public void addExcludeTag(String excludeTag,Integer scope) {
		if(!Strings.isNullOrEmpty(excludeTag)){
			excludeTags.put(excludeTag,null ==scope?Type.ALL.mask:scope);
		}
	}
	public void removeExcludeTag(String excludeTag){
		excludeTags.remove(excludeTag);
	}
	public void addExcludeTag(String excludeTag,Type type) {
		addExcludeTag(excludeTag,(null ==type?Type.ALL:type).mask);
	}
	public void addExcludeTag(String excludeTag,Collection<Type> type) {
		addExcludeTag(excludeTag,Type.sum(type));
	}
	public void addExcludeTag(String excludeTag,String type) {
		addExcludeTag(excludeTag,Strings.isNullOrEmpty(type)?Type.ALL:Type.valueOf(type));
	}
	public void addExcludeTagString(String excludeTag,Collection<String> type) {
		if(null == type || type.isEmpty()){
			addExcludeTag(excludeTag,Type.ALL);
		}
		addExcludeTag(excludeTag,Type.sumOfString(type));
	}
	public void addExcludeTag(String excludeTag) {
		addExcludeTag(excludeTag,Type.ALL);
	}
	public void addExcludeTags(Map<String,Integer> excludeTag) {
		if(null != excludeTag)
			excludeTags.putAll(excludeTag);
	}

	public void additionalText(String text,Action action, Integer scope) {
		if(!Strings.isNullOrEmpty(text)){
			additionalTextTable.put(text, AddColumn.ACTION, action);
			additionalTextTable.put(text, AddColumn.SCOPE, scope);			
		}
	}
	public void additionalText(String text,Action action, Type... type) {
		additionalText(text,action, (null ==type?Type.ALL.mask:Type.sum(type)));
	}
	public void additionalText(String text,String action, String type) {
		additionalText(text,
				Strings.isNullOrEmpty(action)?Action.ADD:Action.valueOf(action), 
				null ==type?Type.ALL:Type.valueOf(type));
	}
	public void additionalText(String text,Action action, Collection<Type> type) {
		additionalText(text,action, (null ==type?Type.ALL.mask:Type.sum(type)));
	}	
	public void additionalText(String text,String action, Collection<String> type) {
		additionalText(text,
				Strings.isNullOrEmpty(action)?Action.ADD:Action.valueOf(action), 
				null ==type?Type.ALL.mask:Type.sumOfString(type));
	}

	/**
	 * @return classDoc
	 */
	TypeElement getClassDoc() {
		return classDoc;
	}
}
